import os
from io import IOBase
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Submit
from django import forms
from django.contrib.auth.models import ContentType
from django.forms import ValidationError, widgets
from django.templatetags.static import static
from django.utils.html import format_html
from django.core.files.storage import default_storage
from django.conf import settings
from django.shortcuts import reverse
from django.utils.translation import gettext_lazy as _

from user.models import UserProfile
from .utils import store_permanents
from lib.django.forms import CrispyModelForm
import json

from media.models import File

class FileUploadWidget(widgets.Input):
    """Widget that renders the FilePond uploader"""

    @property
    def media(self):
        return forms.Media(
            css={
                'all': [
                    static('style/form.min.css'),
                ]
            },
            js=[
                format_html(# filter only temporary files
                    '<script type="module" src="{}"></script>', 
                    static("script/components/file-uploader-autoload.js")
                )
            ]
        )

    def value_from_datadict(self, data, files, name):
        """
        Given a dictionary of data and this widget's name, return the value
        of this widget or None if it's not provided.
        """
        if name in data:
            value = data.getlist(name)
            # Filter out empty values (important, especially in fromset creating new form)
            value = [v for v in value if v]
            return value if value else None
        return None

    def get_context(self, name, value, attrs):
        ctx = super().get_context(name, value, attrs)
        ctx['widget'].update({
            'type': 'hidden',
            'attrs': {
                **ctx['widget']['attrs'],
                'data-file-uploader': '',

            }
        })
        return ctx

class FileUploadField(forms.CharField):
    widget = FileUploadWidget
    
    def __init__(self, *, multiple=False, allowed_types=[], required=False, **kwargs):
        self.multiple = multiple
        self.allowed_types = allowed_types
        super().__init__(required=required, **kwargs)

    def widget_attrs(self, widget):
        attrs = super().widget_attrs(widget)
        attrs.update({
            'data-multiple': str(self.multiple).lower(),
            # NOTE we cannot use `accepted-types` here since it seems that filepond will
            # generate `accept` attribute on `input`, which mess the file type validation
            'data-allowed-types': " ".join(self.allowed_types)
        })
        return attrs
    
    def clean(self, value):
        """
        Validate that all provided file IDs exist in temporary directory
        Always a list of id(str) or None
        """
        if not value:
            return None if self.required else []
            
        if isinstance(value, str):
            value = [value]
        elif not isinstance(value, (list, tuple)):
            return None
            
        for file_id in value:
            try:
                file_path = settings.MEDIA_TMP_UPLOAD_ROOT / file_id
                
                # if file is a temporary file, then file_id contains `/`
                if os.path.dirname(file_id) and not os.path.exists(file_path):
                    raise ValidationError(f"Temporary file {file_id} not found")
                
            except ValueError:
                raise ValidationError(f"Invalid file ID format: {file_id}")
            
        return value

    
class FileUploadModelForm(CrispyModelForm):
    def __init__(self, *args, **kwargs):
        self.has_stored_upload_files = False
        instance = kwargs.get('instance')
        
        super().__init__(*args, **kwargs)

        # Initialize file fields with existing files
        if instance:
            for field_name, field in self.fields.items():
                if isinstance(field, FileUploadField):
                    files = File.objects.get_for_object(instance, field_name)
                    
                    if files.exists():
                        files = list(files)
                        if not field.multiple:
                            files = files[:1]
                        self.initial[field_name] = json.dumps([
                            {
                                "source": str(file.uuid),
                                'options': {
                                    'type': 'local',
                                    'file': {
                                        "name": file.filename,
                                        "size": file.file.size,
                                        "type": file.content_type,
                                    },
                                    "metadata": {
                                        # for filepond-plugin-get-file to download file
                                        "url": reverse('media:raw', kwargs={'pk': file.uuid}),
                                        "uploaded_at": file.uploaded_at.isoformat(),
                                    },
                                }
                            }
                            for file in files
                        ])

    def store_upload_files(self, instance, user: UserProfile):
        if self.has_stored_upload_files:
            return
        
        for field_name, field in self.fields.items():
            if isinstance(field, FileUploadField):
                file_ids = self.cleaned_data.get(field_name, [])
                store_permanents(file_ids, instance, field_name, user, clean_previous=True)

        self.has_stored_upload_files = True
        
class UploadFileForm(forms.Form):
    files = FileUploadField(
        multiple=True,
        label=_("文件"),
    )

    def __init__(self, *args, **kwargs):
         super().__init__(*args, **kwargs)
         self.helper = FormHelper()
         self.helper.form_id = "upload_form"
         submit_btn = Submit('submit', _("上传"))
         submit_btn.field_classes = 'tw:btn tw:btn-neutral'
         self.helper.add_input(submit_btn)

